package de.jmda.core.util.xl;

import static de.jmda.core.util.StringUtil.sb;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;

import com.google.common.collect.ArrayTable;
import com.google.common.collect.Table;

public abstract class AbstractWorkbookReader<T> implements WorkbookReader<T>
{
	private final List<ArrayTable<Integer, Integer, T>> tables =
			new ArrayList<ArrayTable<Integer, Integer, T>>();

	public AbstractWorkbookReader(String file) throws IOException
	{
		super();

		init(Util.workbook(file));
	}

	public AbstractWorkbookReader(File file) throws IOException
	{
		super();

		init(Util.workbook(file));
	}

	@Override
	public List<ArrayTable<Integer, Integer, T>> getSheets()
	{
		return tables;
	}

	protected abstract T asT(Cell cell);

	private void init(Workbook workbook)
	{
		for (int i = 0; i < workbook.getNumberOfSheets(); i++)
		{
			add(workbook.getSheetAt(i));
		}
	}

	private void add(Sheet sheet)
	{
		ArrayTable<Integer, Integer, T> table =
				ArrayTable.create(
						range(Util.rowCount(sheet)), range(Util.colCount(sheet)));

		// getLastRowNum and rowIdx are 0 based, therefore "rowIdx <=" not "rowIdx <"
		for (int rowIdx = 0; rowIdx <= sheet.getLastRowNum(); rowIdx++)
		{
			Row row = sheet.getRow(rowIdx);

			if (row == null)
			{
				continue;
			}

			// getLastCellNum is, unlike colIdx, 1 based, therefore "colIdx <" not "colIdx <="
			for (int colIdx = 0; colIdx < row.getLastCellNum(); colIdx++)
			{
				Cell cell = row.getCell(colIdx);

				if (cell == null)
				{
					table.put(rowIdx, colIdx, null);
				}
				else
				{
					table.put(rowIdx, colIdx, asT(cell));
				}
			}
		}

		tables.add(table);
	}

	public static Iterable<Integer> range(int stop)
	{
		List<Integer> result = new ArrayList<>(stop);

		for (int i = 0; i < stop; i++)
		{
			result.add(i);
		}

		return result;
	}

	public static StringBuffer dump(Table<Integer, Integer, String> table)
	{
		StringBuffer result = sb();

		for (Integer rowkey : table.rowKeySet())
		{
			Map<Integer, String> row = table.row(rowkey);

			for (Integer colkey : row.keySet())
			{
				result.append(
						"[" + rowkey + "," + colkey + "," + table.get(rowkey, colkey) + "]");
			}

			result.append(System.lineSeparator());
		}

		return result;
	}
}